package cn.roadtrip.utilmodule.util;

import cn.hutool.crypto.digest.MD5;
import cn.roadtrip.uredismodule.util.RedisUtil;
import com.google.gson.Gson;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * jwt验证
 */
@Component
@ConditionalOnClass(value = RedisUtil.class)
public class JWTHelper {
    @Autowired
    private RedisUtil redisUtil;
    private static final String Redis_PreToken = "token:session:";
    private static final Long Redis_Timeout = 60 * 60 * 2 + 3l;//延时2小时
    public static final String h256key = "cn.travel";


    public Boolean existKeyTokenValue(TokenValue tokenValue) {
        try {
            Boolean exisit = redisUtil.existKey(Redis_PreToken + tokenValue.getDirect() + ":" + tokenValue.getUserId() + ":" + tokenValue.getLoginCode());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 初始化token值
     *
     * @param userId
     * @param loginCode
     * @param properties
     * @return
     */
    public String initTockenCode(String userId, String direct, String loginCode, String agent, String properties) {
        long initTime = System.currentTimeMillis();
        TokenValue tokenValue = TokenValue.builder()
                .initTime(initTime)
                .direct(direct)
                .loginCode(loginCode)
                .timeout(Redis_Timeout * 1000)
                .userId(userId)
                .agent(agent)
                .build();
//        System.out.println("登录预设超时时间：" + Redis_Timeout);
        redisUtil.set(Redis_PreToken + tokenValue.getDirect() + ":" + tokenValue.getUserId() + ":" + loginCode, properties, Redis_Timeout);
        return createCode(tokenValue);
    }

    /**
     * 初始化token值
     *
     * @param userId
     * @param loginCode
     * @param properties
     * @return
     */
    public String initTockenCode(String userId, String direct, String loginCode, String agent, java.util.Map<String, Object> properties) {
        Gson gson = new Gson();
        long initTime = System.currentTimeMillis();
        TokenValue tokenValue = TokenValue.builder()
                .initTime(initTime)
                .direct(direct)
                .loginCode(loginCode)
                .timeout(Redis_Timeout * 1000)
                .userId(userId)
                .agent(agent)
                .build();
//        System.out.println("登录预设超时时间：" + Redis_Timeout);
        redisUtil.set(Redis_PreToken + tokenValue.getDirect() + ":" + tokenValue.getUserId() + ":" + loginCode, gson.toJson(properties), Redis_Timeout);
        return createCode(tokenValue);
    }

    /**
     * 获取当前用户的登录属性
     *
     * @param userId
     * @return
     */
    public java.util.Map<String, Object> getCurrentTokenProperty(String userId, String direct, String clientCode) {
//        System.out.println("rediskey->" + Redis_PreToken + userId + ":" + clientCode);
        String str = redisUtil.get(Redis_PreToken + direct + ":" + userId + ":" + clientCode);
        Gson gson = new Gson();
        java.util.HashMap<String, Object> rs = gson.fromJson(str, java.util.HashMap.class);
        return rs;
    }

    private ReentrantLock reentrantLock = new ReentrantLock();

    /**
     * 刷新token
     *
     * @param userId     用户ID
     * @param clientCode 登录码
     * @return
     */
    public String freshTokenTime(String userId, String direct, String clientCode, String agent) {
        try {
            reentrantLock.tryLock(5, TimeUnit.SECONDS);
            Gson gson = new Gson();
            long initTime = System.currentTimeMillis();
            TokenValue tokenValue = TokenValue.builder()
                    .initTime(initTime)
                    .loginCode(clientCode)
                    .direct(direct)
                    .timeout(Redis_Timeout * 1000)
                    .userId(userId)
                    .agent(agent)
                    .build();
            Long t = System.currentTimeMillis();
            String c = Redis_PreToken + direct + ":" + tokenValue.getUserId() + ":" + clientCode;
//            System.out.println("print code->" + c);
            String properties = redisUtil.get(c);
            if (properties == null) {
                try {
                    int ms = new Random(System.currentTimeMillis()).nextInt(180);
                    Thread.sleep(ms + 60);
                    properties = redisUtil.get(Redis_PreToken + direct + ":" + tokenValue.getUserId() + ":" + clientCode);
                } catch (Exception e) {
                }
            }
            Boolean b = redisUtil.deleteKey(c);
            redisUtil.set(c, properties, Redis_Timeout);
            return createCode(tokenValue);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            reentrantLock.unlock();
        }
    }

    public Boolean containsPropertyKey(String token, String direct, String clientCode, String key, Object value) {
        TokenValue tokenValue = parseToken(token);
        String properties = redisUtil.get(Redis_PreToken + direct + ":" + tokenValue.getUserId() + ":" + clientCode);
        Gson gson = new Gson();
        java.util.HashMap<String, Object> map = null;
        try {
            map = gson.fromJson(properties, java.util.HashMap.class);
            return map.get(key).equals(value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            map = null;
        }
    }

    public Object getProperty(String token, String direct, String clientCode, String key) {
        TokenValue tokenValue = parseToken(token);
//        System.out.println("[redis]->" + Redis_PreToken + tokenValue.getUserId() + ":" + clientCode);
        String properties = redisUtil.get(Redis_PreToken + direct + ":" + tokenValue.getUserId() + ":" + clientCode);
        if (properties == null) {
            try {
                int ms = new Random(System.currentTimeMillis()).nextInt(180);
                Thread.sleep(ms + 60);
                properties = redisUtil.get(Redis_PreToken + direct + ":" + tokenValue.getUserId() + ":" + clientCode);
            } catch (Exception e) {
            }
        }
        Gson gson = new Gson();
        java.util.HashMap<String, Object> map = null;
        try {
            map = gson.fromJson(properties, java.util.HashMap.class);
            return map.get(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            map = null;
        }
    }

    /**
     * 是否存在属性
     *
     * @param token
     * @param key
     * @return
     */
    public Boolean existProperty(String token, String direct, String clientCode, String key) {
        TokenValue tokenValue = parseToken(token);
        String properties = redisUtil.get(Redis_PreToken + direct + ":" + tokenValue.getUserId() + ":" + clientCode);
        if (properties == null) {
            try {
                int ms = new Random(System.currentTimeMillis()).nextInt(180);
                Thread.sleep(ms + 60);
                properties = redisUtil.get(Redis_PreToken + direct + ":" + tokenValue.getUserId() + ":" + clientCode);
            } catch (Exception e) {
            }
        }
        Gson gson = new Gson();
        java.util.HashMap<String, Object> map = null;
        try {
            map = gson.fromJson(properties, java.util.HashMap.class);
            return map.containsKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            map = null;
        }
    }

    /**
     * 数组数据是否在属性中存在
     *
     * @param token
     * @param clientCode
     * @param key        属性名
     * @param datas      要匹配的数据数组
     * @return
     */
    public Boolean existListDataInProperty(String token, String direct, String clientCode, String key, String[] datas) {
        if (datas == null || datas.length == 0) {
            return true;
        }
        boolean allBlock = true;
        for (String data : datas) {
            if (data == null) {
                continue;
            }
            if (data.trim().equals(""))
                continue;
            allBlock = false;
            break;
        }
        if (allBlock)
            return true;
        TokenValue tokenValue = parseToken(token);
        String properties = redisUtil.get(Redis_PreToken + direct + ":" + tokenValue.getUserId() + ":" + clientCode);
        Gson gson = new Gson();
        java.util.HashMap<String, Object> map = null;
        try {
            map = gson.fromJson(properties, java.util.HashMap.class);
            if (!map.containsKey(key))
                return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            map = null;
        }
        java.util.List<String> list = null;
        try {
            list = (java.util.List<String>) map.get(key);
        } catch (Exception e) {
            return false;
        }
        if (list == null || list.isEmpty()) {
            return false;
        }
        for (String data : datas) {
            if (list.contains(data))
                return true;
        }
        return false;
    }

    /**
     * 创建token
     *
     * @param tokenValue
     * @return
     */
    private String createCode(TokenValue tokenValue) {
        Gson gson = new Gson();
        String token = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, MD5.create().digestHex(h256key))
                .claim("content", gson.toJson(tokenValue))
                .setExpiration(new Date(tokenValue.getTimeout() + System.currentTimeMillis()))//token过期时间
                .compact();
//        System.out.println("实际最后超时时间：" + (new Date(tokenValue.getTimeout() + System.currentTimeMillis())));
        return token;
    }

    private String createCode(TokenValue tokenValue, Long endtime) {
        Gson gson = new Gson();
        String token = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, MD5.create().digestHex(h256key))
                .claim("content", gson.toJson(tokenValue))
                .setExpiration(new Date(endtime))//token过期时间
                .compact();
//        System.out.println("实际最后超时时间：" + (new Date(tokenValue.getTimeout() + System.currentTimeMillis())));
        return token;
    }


    public boolean hasKeyValue(String str, String key, String value) {
        try {
            Claims claims = null;
            claims = Jwts.parser().setSigningKey(MD5.create().digestHex(h256key)).parseClaimsJws(str)
                    .getBody();
            if (claims.get("content", String.class) == null) {
                return false;
            }
            Gson gson = new Gson();
            str = claims.get("content", String.class);
            java.util.Map<String, Object> map = gson.fromJson(str, java.util.Map.class);
            return map.get(key).equals(value);
        } catch (Exception e) {
            return false;
        }
    }

    public Boolean checkToken(String str) {
        try {
            Claims claims = null;
            claims = Jwts.parser().setSigningKey(MD5.create().digestHex(h256key)).parseClaimsJws(str)
                    .getBody();
            if (claims.get("content", String.class) == null) {
                return false;
            }
            Gson gson = new Gson();
            str = claims.get("content", String.class);
            TokenValue t = null;
            t = gson.fromJson(str, TokenValue.class);
            if ((t.getTimeout() + t.getInitTime()) < System.currentTimeMillis()) {
//                System.out.println("time-limit->" + DateUtil.format(new Date(t.getTimeout()), "yyyy-MM-dd HH:mm:ss"));
                return false;
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 解析token
     *
     * @param str
     * @return
     */
    public TokenValue parseToken(String str) {
        Claims claims = null;
        claims = Jwts.parser().setSigningKey(MD5.create().digestHex(h256key)).parseClaimsJws(str)
                .getBody();
        Gson gson = new Gson();
        TokenValue t = null;
        str = claims.get("content", String.class);
        t = gson.fromJson(str, TokenValue.class);
        return t;
    }
}